package cn.wyz.basic.service.impl;

import cn.wyz.basic.constants.BaseConstants;
import cn.wyz.basic.dto.RegisterDto;
import cn.wyz.basic.exception.BusinessException;
import cn.wyz.basic.service.IVerifyCodeService;
import cn.wyz.basic.util.SmsUtils;
import cn.wyz.basic.util.StrUtils;
import cn.wyz.basic.util.VerifyCodeUtils;
import cn.wyz.user.domain.User;
import cn.wyz.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;
@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Override
    public String getImgCode(String key) {
        //key是前端传回来的UUid,这个东西正常访问一定会生成,这样是防止别人直接攻击接口
        if (!StringUtils.hasLength(key)) {
            throw new BusinessException("key不能为空!!");
        }
        //1.每次点图形验证码都是直接生成新的code,但是要对前面的code覆盖
        String code = StrUtils.getComplexRandomString(4);
        //2.把这个code保存到redis中,key就是uuid
        redisTemplate.opsForValue().set(
                key,//前端传回的UUid
                code,//刚才调用strUtils方法得到的随机4位数
                5,//设置过期的时间长度
                TimeUnit.MINUTES//时间计量单位
        );
        //3.使用2D引擎把code生成为一个图片,再把这个图片用Base64编码成字符串
        String verifyCode = VerifyCodeUtils.VerifyCode(100, 38, code);
        return verifyCode;
    }

    /**
     * 实现手机验证码发送
     * 先要在校验图片验证码成功的情况下,才对短信验证码进行验证
     * 一：校验
     * 1.空校验
     * 2.校验图形验证码，根据imageCodeKey从redis获取图形验证码
     * 。获取不到 图形验证码过期，重新获取
     * 。获取到了，比对图形验证码是否正确
     * - 不正确，提示输入图形验证码错误
     * - 正确，往下走
     * 3.校验手机号是否注册过了
     * 。注册过了，滚去登录
     * 。没有注册过，往下走
     * <p>
     * 二：短信验证码逻辑
     * 1.根据手机号拼接key，159:register，从redis获取曾今可能发送的短信验证码
     * 1.1 获取到了
     * 1.1.1 校验是否过了一分钟重发时间 value  9527:232468742341657213
     * 。小于一分钟  哥屋恩
     * 。大于一分中 （还没有到3分钟，验证码还有效），
     * 使用还没过期的验证码，刷新过期时间
     * 1.2 获取不到
     * 1.2.1.生成新的短信验证码
     * <p>
     * 3.存储redis 设置一个过期时间
     * 4.发送短信
     *
     * @param dto
     */
    @Override//主方法
    public void smsCode(RegisterDto dto) {
        String type = dto.getType();
        if(StringUtils.isEmpty(type)){
            throw new BusinessException("业务类型不能为空");
        };
        if (type.equals("binder")) {
            sendBinder(dto);
        };
        if(type.equals("register")){
            sendRegister(dto);
        }


        /*//1.图形验证码空校验  1.是否填写手机号 2.是否获取到UUid(codeKey) 3.是否填入了图形验证码codeValue
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(imageCodeKey) || StringUtils.isEmpty(imageCodeValue)) {
            throw new BusinessException("参数不能为空");
        }
        //2.校验图形验证码，根据imageCodeKey从redis获取用于比对的图形验证码
        Object imageCodeKeyTmp = redisTemplate.opsForValue().get(imageCodeKey);
        //获取不到 图形验证码,表示redis中的key过期，重新获取
        if (imageCodeKeyTmp.toString().isEmpty()) {
            throw new BusinessException("图形验证码已经失效，请重新获取！！");
        }
        //获取到了，比对图形验证码是否正确- equalsIgnoreCase不区分大小写的比较不正确，提示输入图形验证码错误
        if (!imageCodeKeyTmp.toString().equalsIgnoreCase(imageCodeValue)) {
            throw new BusinessException("图形验证码填写错误,请重新填写");
        }
        //3.校验手机号是否注册过了-只在user用户表里面查,查不到数据表示没注册
        User user = userMapper.selectByPhone(phone);
        if (user != null) {
            throw new BusinessException("该手机号已经注册,请直接登录");
        }
//----------------  以上是图片验证码的验证部分,全部通过,才进入下面手机验证码验证部分  -------------------------------------
//-------------------------------------------------------------------------------------------------------------
        //1.根据手机号拼接key，register:13548211415，从redis获取曾今可能发送的短信验证码
        String registerPhoneKey = BaseConstants.RegisterConstants.REGISTER_SMS_PREFIX + phone;
        //从redis获取曾今可能发送的短信验证码值
        Object registerPhoneValueTMP = redisTemplate.opsForValue().get(registerPhoneKey);
        //1.1 获取到了旧的随机码字符串,创建一个新的随机码字符串
        String newRegisterPhoneValue = "";
        if (registerPhoneValueTMP != null) {
            //1.1.1小于一分钟:(对类型进行转换,因为拿到的系统时间戳是个long类型)--这是拿到验证码后面的时间戳  oldValue  9527:232468742341657213
            Long oldValue = Long.valueOf(registerPhoneValueTMP.toString().split(":")[1]);  //232468742341657213
            //重新校验二次获取到验证码的时间是否超时 1分钟,超过表示绕过前端的定时器,直接进入的,视为非法操作
            if (System.currentTimeMillis() - oldValue <= 1 * 60 * 1000) {
                throw new BusinessException("你正在进行非法操作,没有活儿可以咬打火机嗷");
            }
            //1.1.1大于一分钟,是正常在前端获取的,且能获取到旧验证码表示还没有过期(3分钟)那么,直接吧旧的发过去
            newRegisterPhoneValue = registerPhoneValueTMP.toString().split(":")[0];//newValue  9527
        } else {
            //1.2 生成新的4位随机码
            newRegisterPhoneValue = StrUtils.getComplexRandomString(4);
        }
//------------  以上对手机验证码校验完成,接下来要把新的registerPhoneValue存在redis中  ----------------------------------
        //3.新的registerPhoneValue存储到redis ,要拼上系统时间戳,并重置redis里的时间
        String smsCodeValue = newRegisterPhoneValue + ":" + System.currentTimeMillis();
        redisTemplate.opsForValue().set(
                registerPhoneKey,
                smsCodeValue,
                3,
                TimeUnit.MINUTES
        );
        //4.发送短信,要给钱,先注了
//        SmsUtils.sendSms(phone,"亲,您的验证码是:"+newRegisterPhoneValue+"。请在3分钟内使用该验证码进行注册");
        System.out.println("发送成功.亲,您的验证码是:" + newRegisterPhoneValue + "。请在3分钟内使用该验证码进行注册");*/
    }

    /**
     * 微信登录绑定调用
     */
    public void sendBinder(RegisterDto dto){
        String phone = dto.getPhone();
        String businessKey= BaseConstants.RegisterConstants.REGISTER_SMS_PREFIX+phone;;
        commonSendMessage(businessKey);
    }

    /**
     * 密码注册调用
     */
    public void sendRegister(RegisterDto dto){
        String phone = dto.getPhone();
        String imageCodeKey = dto.getImageCodeKey();
        String imageCodeValue = dto.getImageCodeValue();
        String businessKey= BaseConstants.RegisterConstants.REGISTER_SMS_PREFIX+phone;
        //1.图形验证码空校验  1.是否填写手机号 2.是否获取到UUid(codeKey) 3.是否填入了图形验证码codeValue
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(imageCodeKey) || StringUtils.isEmpty(imageCodeValue)) {
            throw new BusinessException("参数不能为空");
        }
        //2.校验图形验证码，根据imageCodeKey从redis获取用于比对的图形验证码
        Object imageCodeKeyTmp = redisTemplate.opsForValue().get(imageCodeKey);
        //获取不到 图形验证码,表示redis中的key过期，重新获取
        if (imageCodeKeyTmp.toString().isEmpty()) {
            throw new BusinessException("图形验证码已经失效，请重新获取！！");
        }
        //获取到了，比对图形验证码是否正确- equalsIgnoreCase不区分大小写的比较不正确，提示输入图形验证码错误
        if (!imageCodeKeyTmp.toString().equalsIgnoreCase(imageCodeValue)) {
            throw new BusinessException("图形验证码填写错误,请重新填写");
        }
        //3.校验手机号是否注册过了-只在user用户表里面查,查不到数据表示没注册
        User user = userMapper.selectByPhone(phone);
        if (user != null) {
            throw new BusinessException("该手机号已经注册,请直接登录");
        }
        commonSendMessage(businessKey);
    }

    /**
     * 公共:发送手机验证码
     */
    public void commonSendMessage (String businessKey){
        //从redis获取曾今可能发送的短信验证码值
        Object registerPhoneValueTMP = redisTemplate.opsForValue().get(businessKey);
        //1.1 获取到了旧的随机码字符串,创建一个新的随机码字符串
        String newRegisterPhoneValue = "";
        if (registerPhoneValueTMP != null) {
            //1.1.1小于一分钟:(对类型进行转换,因为拿到的系统时间戳是个long类型)--这是拿到验证码后面的时间戳  oldValue  9527:232468742341657213
            Long oldValue = Long.valueOf(registerPhoneValueTMP.toString().split(":")[1]);  //232468742341657213
            //重新校验二次获取到验证码的时间是否超时 1分钟,超过表示绕过前端的定时器,直接进入的,视为非法操作
            if (System.currentTimeMillis() - oldValue <= 1 * 60 * 1000) {
                throw new BusinessException("你正在进行非法操作,没有活儿可以咬打火机嗷");
            }
            //1.1.1大于一分钟,是正常在前端获取的,且能获取到旧验证码表示还没有过期(3分钟)那么,直接吧旧的发过去
            newRegisterPhoneValue = registerPhoneValueTMP.toString().split(":")[0];//newValue  9527
        } else {
            //1.2 生成新的4位随机码
            newRegisterPhoneValue = StrUtils.getComplexRandomString(4);
        }
//------------  以上对手机验证码校验完成,接下来要把新的registerPhoneValue存在redis中  ----------------------------------
        //3.新的registerPhoneValue存储到redis ,要拼上系统时间戳,并重置redis里的时间
        String smsCodeValue = newRegisterPhoneValue + ":" + System.currentTimeMillis();
        redisTemplate.opsForValue().set(
                businessKey,
                smsCodeValue,
                3,
                TimeUnit.MINUTES
        );
        //4.发送短信,要给钱,先注了
//        SmsUtils.sendSms(phone,"亲,您的验证码是:"+newRegisterPhoneValue+"。请在3分钟内使用该验证码进行注册");
        System.out.println("发送成功.亲,您的验证码是:" + newRegisterPhoneValue + "。请在3分钟内使用该验证码进行注册");
    }
}
